﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using CH.Easy.DbProvider.Common;
using CH.Easy.DbProvider.ExpressionExtend;
using System.Reflection;

namespace CH.Easy.DbProvider.Core
{

    public class ExpressionAnalyseFactory 
    {
        public static ExpressionAnalyse Create(Expression expression) 
        {
            switch ((ExpressionTypeExend)expression.NodeType)
            {
                case ExpressionTypeExend.Insert:
                case ExpressionTypeExend.Modify:
                case ExpressionTypeExend.Delete:
                    break;
                default:
                    expression = new SelectExpression(expression);
                    break;

            }
            string typeName = "CH.Easy.DbProvider.Core." + ((ExpressionTypeExend)expression.NodeType).ToString() + "ExpressionAnalyse";
            ExpressionAnalyse ea = ((ExpressionAnalyse)Activator.CreateInstance(Assembly.GetExecutingAssembly().GetType(typeName)));
            ea.expression = expression;
            return ea;
        }
    }

    public abstract class ExpressionAnalyse : DbExpressionVisitor
    {
        public Expression expression { get; set; }
        public ExpressionAnalyse()
        {
            
        }
        public abstract LambdaExpression GetResultExpression();
        public abstract ExecuteType GetExecuteType();
        public abstract Expression Work();


    }
    public class InsertExpressionAnalyse : ExpressionAnalyse
    {
     
        public InsertExpressionAnalyse()
        {
            
        }

      

        public override ExecuteType GetExecuteType()
        {
            return ExecuteType.ExecuteScalar;
        }

        public override LambdaExpression GetResultExpression()
        {
            return null;
        }


        public override Expression Work()
        {
            return expression;
        }

       
    }
    public class ModifyExpressionAnalyse : ExpressionAnalyse
    {

        public ModifyExpressionAnalyse()
        {
           
        }
        public override LambdaExpression GetResultExpression()
        {
            return null;
        }

        public override ExecuteType GetExecuteType()
        {
            return ExecuteType.ExecuteNonQuery;
        }
        public override Expression Work()
        {
            return expression;
        }
    }
    public class DeleteExpressionAnalyse : ExpressionAnalyse
    {

        public DeleteExpressionAnalyse()
        {

        }
        public override LambdaExpression GetResultExpression()
        {
            return null;
        }
        public override ExecuteType GetExecuteType()
        {
            return ExecuteType.ExecuteNonQuery;
        }

        public override Expression Work()
        {
            return expression;
        }
    }
    public class SelectExpressionAnalyse : ExpressionAnalyse
    {

        private SelectExpression se { get; set; }
        private LambdaExpression resultExpression;
        private ExecuteType executeType;
        public SelectExpressionAnalyse()
        {
            
        }

        public override LambdaExpression GetResultExpression()
        {
            if (resultExpression == null)
            {
                ParameterExpression p = Expression.Parameter(this.se.From.TableType, "p");
                this.resultExpression = Expression.Lambda(p, p);
            }

            return this.resultExpression;
        }

        public override ExecuteType GetExecuteType()
        {
            if (this.executeType == ExecuteType.None) 
            {
                this.executeType = ExecuteType.ExecuteReader;
            }
            return this.executeType;
        }


        public override Expression Work()
        {
            se = expression as SelectExpression;
            this.Visit(expression);
            return se;
        }
        protected override Expression VisitMethodCall(MethodCallExpression m)
        {

            if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Take")
            {
                se.Take = new TakeExpression(m.Arguments[1]);
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Skip")
            {
                se.Skip = new SkipExpression(m.Arguments[1]);
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where")
            {
                if (se.Where.Expression == null)
                {
                    se.Where = new WhereExpression(StripQuotes(m.Arguments[1]));
                }
                else
                {
                    se.Where = new WhereExpression(Expression.And(((LambdaExpression)se.Where.Expression).Body, ((LambdaExpression)StripQuotes(m.Arguments[1])).Body));
                }
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Select")
            {
                if (se.Column.Expression == null)
                {

                    se.Column = new ColumnExpression(m.Arguments[1]);
                    this.resultExpression = (LambdaExpression)StripQuotes(m.Arguments[1]);
                    this.executeType = ExecuteType.ExecuteReader;
                    this.Visit(m.Arguments[0]);
                }
                else
                {
                    ExpressionAnalyse ea = ExpressionAnalyseFactory.Create(m);
                    se.From = new FromExpression(ea.Work());
                }
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Count")
            {
                if (m.Arguments.Count > 1)
                {
                    if (se.Where.Expression == null)
                    {
                        se.Where = new WhereExpression(StripQuotes(m.Arguments[1]));
                    }
                    else
                    {
                        se.Where = new WhereExpression(Expression.And(se.Where.Expression, StripQuotes(m.Arguments[1])));
                    }
                    se.Column = new ColumnExpression(Expression.Call(null, typeof(MethodExtend).GetMethod("Count"), Expression.Constant(1)));
                }
                else
                {
                    se.Column = new ColumnExpression(m);
                }


                this.executeType = ExecuteType.ExecuteScalar;
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && (m.Method.Name == "Sum" || m.Method.Name == "Average" || m.Method.Name == "Max" || m.Method.Name == "Min"))
            {
                se.Column = new ColumnExpression(m);
                this.executeType = ExecuteType.ExecuteScalar;
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && (m.Method.Name == "OrderBy" || m.Method.Name == "ThenBy"))
            {
                se.Order.AddOrder(Expression.Call(typeof(MethodExtend).GetMethod("Order"), m.Arguments[1], Expression.Parameter(typeof(OrderType), OrderType.ASC.ToString())));
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && (m.Method.Name == "OrderByDescending" || m.Method.Name == "ThenByDescending"))
            {
                if (se.Order == null)
                {
                    se.Order = new OrderExpression();
                }
                se.Order.AddOrder(Expression.Call(typeof(MethodExtend).GetMethod("Order"), m.Arguments[1], Expression.Parameter(typeof(OrderType), OrderType.DESC.ToString())));
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "GroupBy")
            {
                se.Group = new GroupExpression(m.Arguments[1]);
                this.Visit(m.Arguments[0]);
            }
            else if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Join")
            {
                se.Column = new ColumnExpression(m.Arguments[4]);
                se.InnerJoin = new InnerJoinExpression(m, m.Arguments[0], m.Arguments[1], m.Arguments[2], m.Arguments[3], m.Arguments[4]);
                this.resultExpression = (LambdaExpression)StripQuotes(m.Arguments[4]);
                this.executeType = ExecuteType.ExecuteReader;


                this.Visit(m.Arguments[0]);
            }
            else 
            {
                this.Visit(m.Arguments[0]);
            }

            return m;
        }
        protected override Expression VisitConstant(ConstantExpression c)
        {

            IQueryable q = c.Value as IQueryable;
            if (q != null)
            {

                se.From = new FromExpression(new SelectExpression(c));
               
            }
            return c;
        }
    }

}
